package com.mc.fastkit.utils

import android.app.Activity
import android.graphics.Rect
import android.os.Build
import android.view.View
import android.view.ViewTreeObserver
import android.view.Window
import android.view.WindowInsets
import android.view.inputmethod.InputMethodManager
import androidx.activity.ComponentActivity
import androidx.annotation.RequiresApi
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import com.mc.fastkit.ext.decorView
import kotlin.math.abs

/**
 * 软键盘工具类
 * @author: MasterChan
 * @date: 2023-01-03 15:04
 */
object KeyboardUtils {

    /**
     * 键盘判断为显示状态时，[Window.getDecorView]被遮挡的最小高度
     */
    var keyboardJudgeShowHeight = 200
    private var decorViewVisibleHeight = 0
    private const val KEY_LISTENER = -8
    private var lastKeyboardHeightR = 0

    fun interface OnKeyboardChangedListener {
        fun onKeyboardChanged(isShow: Boolean, height: Int)
    }

    /**
     * 显示键盘
     * @param view 接收输入的View
     * @param flags 默认为0，[InputMethodManager.SHOW_IMPLICIT]有可能不会显示；
     * [InputMethodManager.SHOW_FORCED]强制显示
     */
    fun showKeyboard(view: View, flags: Int = 0) {
        val manager = view.context.getSystemService(InputMethodManager::class.java) ?: return
        view.isFocusable = true
        view.isFocusableInTouchMode = true
        view.requestFocus()
        manager.showSoftInput(view, flags)
    }

    fun hideKeyboard(view: View) {
        val imm = view.context.getSystemService(InputMethodManager::class.java) ?: return
        imm.hideSoftInputFromWindow(view.windowToken, 0)
    }

    fun hideKeyboard(window: Window) {
        val imm = window.decorView.context.getSystemService(InputMethodManager::class.java)
                  ?: return
        imm.hideSoftInputFromWindow(window.decorView.windowToken, 0)
    }

    fun isShown(activity: Activity): Boolean {
        return isShown(activity.window)
    }

    fun isShown(window: Window): Boolean {
        return getDecorViewInvisibleHeight(window) > keyboardJudgeShowHeight
    }

    /**
     * 获取[Window.getDecorView]未显示区域的高度；未显示区域为键盘遮挡，即获取的是键盘高度
     * @param window Window
     * @return Int
     */
    private fun getDecorViewInvisibleHeight(window: Window): Int {
        val decorView = window.decorView
        val outRect = Rect()
        decorView.getWindowVisibleDisplayFrame(outRect)
        return abs(decorView.bottom - outRect.bottom)
    }

    @RequiresApi(Build.VERSION_CODES.R)
    private fun registerOnKeyboardChangedListenerR(
        view: View,
        listener: OnKeyboardChangedListener
    ) {
        view.setOnApplyWindowInsetsListener { _, insets ->
            val inset = insets.getInsets(WindowInsets.Type.ime())
            val height = inset.bottom - inset.top
            val isShow = height > 0
            if (isShow) {
                lastKeyboardHeightR = height
                listener.onKeyboardChanged(true, height)
            } else {
                if (lastKeyboardHeightR > 0) {
                    lastKeyboardHeightR = height
                    listener.onKeyboardChanged(false, height)
                }
            }
            insets
        }
    }

    fun registerOnKeyboardChangedListener(activity: Activity, listener: OnKeyboardChangedListener) {
        activity.decorView.viewTreeObserver.addOnGlobalLayoutListener(
            DecorViewLayoutListener(activity.window, listener).apply {
                activity.window.decorView.setTag(KEY_LISTENER, this)
            }
        )
    }

    fun registerOnKeyboardChangedListener(
        activity: ComponentActivity,
        listener: OnKeyboardChangedListener
    ) {
        activity.lifecycle.addObserver(object : DefaultLifecycleObserver {
            override fun onDestroy(owner: LifecycleOwner) {
                unregisterOnKeyboardChangedListener(activity)
            }
        })
        activity.decorView.viewTreeObserver.addOnGlobalLayoutListener(
            DecorViewLayoutListener(activity.window, listener).apply {
                activity.window.decorView.setTag(KEY_LISTENER, this)
            }
        )
    }

    fun registerOnKeyboardChangedListener(window: Window, listener: OnKeyboardChangedListener) {
        window.decorView.viewTreeObserver.addOnGlobalLayoutListener(
            DecorViewLayoutListener(window, listener).apply {
                window.decorView.setTag(KEY_LISTENER, this)
            }
        )
    }

    fun unregisterOnKeyboardChangedListener(activity: Activity) {
        unregisterOnKeyboardChangedListener(activity.window)
    }

    fun unregisterOnKeyboardChangedListener(window: Window) {
        val decorView = window.decorView
        val tag = decorView.getTag(KEY_LISTENER)
        if (tag is ViewTreeObserver.OnGlobalLayoutListener) {
            decorView.viewTreeObserver.removeOnGlobalLayoutListener(tag)
        }
    }

    private class DecorViewLayoutListener(
        private val window: Window,
        private val listener: OnKeyboardChangedListener
    ) : ViewTreeObserver.OnGlobalLayoutListener {
        override fun onGlobalLayout() {
            //获取当前根视图在屏幕上显示的大小
            val rect = Rect()
            window.decorView.getWindowVisibleDisplayFrame(rect)
            val visibleHeight = rect.height()
            if (decorViewVisibleHeight == 0) {
                decorViewVisibleHeight = visibleHeight
                return
            }

            //根视图显示高度没有变化，可以看作软键盘显示／隐藏状态没有改变
            if (decorViewVisibleHeight == visibleHeight) {
                return
            }

            //根视图显示高度变小超过200，可以看作软键盘显示了
            if (decorViewVisibleHeight - visibleHeight > keyboardJudgeShowHeight) {
                listener.onKeyboardChanged(true, decorViewVisibleHeight - visibleHeight)
                decorViewVisibleHeight = visibleHeight
                return
            }

            //根视图显示高度变大超过200，可以看作软键盘隐藏了
            if (visibleHeight - decorViewVisibleHeight > 200) {
                listener.onKeyboardChanged(false, visibleHeight - decorViewVisibleHeight)
                decorViewVisibleHeight = visibleHeight
            }
        }
    }
}